/*
 * FreeModbus Libary: A portable Modbus implementation for Modbus ASCII/RTU.
 * Copyright (C) 2013 Armink <armink.ztl@gmail.com>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * File: $Id: mbrtu_m.c,v 1.60 2013/08/20 11:18:10 Armink Add Master Functions $
 */

/* ----------------------- System includes ----------------------------------*/
#include "stdlib.h"
#include "string.h"

/* ----------------------- Platform includes --------------------------------*/
#include "port.h"

/* ----------------------- Modbus includes ----------------------------------*/

#include "mb.h"
#include "mb_m.h"
#include "mbconfig.h"
#include "mbframe.h"
#include "mbfunc.h"
#include "mbproto.h"

#include "mbport.h"
#if MB_MASTER_RTU_ENABLED == 1
#include "mbrtu.h"
#endif
#if MB_MASTER_ASCII_ENABLED == 1
#include "mbascii.h"
#endif
#if MB_MASTER_TCP_ENABLED == 1
#include "mbtcp.h"
#endif

#if MB_MASTER_RTU_ENABLED > 0 || MB_MASTER_ASCII_ENABLED > 0

#ifndef MB_PORT_HAS_CLOSE
#define MB_PORT_HAS_CLOSE 0
#endif

/* ----------------------- Static variables ---------------------------------*/

static UCHAR ucMBMasterDestAddress; // 这里是从站地址??
static BOOL xMBRunInMasterMode = FALSE; //
static eMBMasterErrorEventType eMBMasterCurErrorType; // 保存modbusmaster当前错误类型

static enum {
    STATE_ENABLED, // 主站使能
    STATE_DISABLED, // 主站失能
    STATE_NOT_INITIALIZED, // 主站没有初始化
    STATE_ESTABLISHED, // 主站建立
} eMBState
    = STATE_NOT_INITIALIZED; // 在eMBMasterInit()函数里面会变为  STATE_DISABLED

/* Functions pointer which are initialized in eMBInit( ). Depending on the
 * mode (RTU or ASCII) the are set to the correct implementations.
 * Using for Modbus Master,Add by Armink 20130813
 */
static peMBFrameSend peMBMasterFrameSendCur;
static pvMBFrameStart pvMBMasterFrameStartCur;
static pvMBFrameStop pvMBMasterFrameStopCur;
static peMBFrameReceive peMBMasterFrameReceiveCur;
static pvMBFrameClose pvMBMasterFrameCloseCur;

/* Callback functions required by the porting layer. They are called when
 * an external event has happend which includes a timeout or the reception
 * or transmission of a character.
 * Using for Modbus Master,Add by Armink 20130813
 */
BOOL(*pxMBMasterFrameCBByteReceived)
(void);
BOOL(*pxMBMasterFrameCBTransmitterEmpty)
(void);
BOOL(*pxMBMasterPortCBTimerExpired)
(void); // 指向函数的指针

BOOL(*pxMBMasterFrameCBReceiveFSMCur)
(void);
BOOL(*pxMBMasterFrameCBTransmitFSMCur)
(void);

// typedef struct
// {
//     UCHAR ucFunctionCode;
//     pxMBFunctionHandler pxHandler;
// } xMBFunctionHandler;

/* An array of Modbus functions handlers which associates Modbus function
 * codes with implementing functions.  //结构体里面的是功能码和函数指针
 */
static xMBFunctionHandler xMasterFuncHandlers[MB_FUNC_HANDLERS_MAX] = {
// 这里是一个数组,不过数组里面的成员是结构体
#if MB_FUNC_OTHER_REP_SLAVEID_ENABLED > 0
    // TODO Add Master function define
    { MB_FUNC_OTHER_REPORT_SLAVEID, eMBFuncReportSlaveID },
#endif
#if MB_FUNC_READ_INPUT_ENABLED > 0
    { MB_FUNC_READ_INPUT_REGISTER, eMBMasterFuncReadInputRegister }, /*0x04   不支持广播模式*/
#endif
#if MB_FUNC_READ_HOLDING_ENABLED > 0
    { MB_FUNC_READ_HOLDING_REGISTER, eMBMasterFuncReadHoldingRegister }, /*0x03     不支持广播模式*/
#endif
#if MB_FUNC_WRITE_MULTIPLE_HOLDING_ENABLED > 0
    { MB_FUNC_WRITE_MULTIPLE_REGISTERS, eMBMasterFuncWriteMultipleHoldingRegister }, /*0x10    支持广播模式*/
#endif //
#if MB_FUNC_WRITE_HOLDING_ENABLED > 0
    { MB_FUNC_WRITE_REGISTER, eMBMasterFuncWriteHoldingRegister }, /*0x06   支持广播模式*/
#endif
#if MB_FUNC_READWRITE_HOLDING_ENABLED > 0
    { MB_FUNC_READWRITE_MULTIPLE_REGISTERS, eMBMasterFuncReadWriteMultipleHoldingRegister },
#endif
#if MB_FUNC_READ_COILS_ENABLED > 0
    { MB_FUNC_READ_COILS, eMBMasterFuncReadCoils }, // 0x01
#endif
#if MB_FUNC_WRITE_COIL_ENABLED > 0
    { MB_FUNC_WRITE_SINGLE_COIL, eMBMasterFuncWriteCoil }, // 0x05     支持广播模式
#endif
#if MB_FUNC_WRITE_MULTIPLE_COILS_ENABLED > 0
    { MB_FUNC_WRITE_MULTIPLE_COILS, eMBMasterFuncWriteMultipleCoils }, // 0x0F  支持广播模式    写指令才支持广播,读指令是不支持广播的
#endif
#if MB_FUNC_READ_DISCRETE_INPUTS_ENABLED > 0
    { MB_FUNC_READ_DISCRETE_INPUTS, eMBMasterFuncReadDiscreteInputs }, // 0x02
#endif
}; // 上面是数组的初始化  上面绑定的函数每一个都得了解了解

/* ----------------------- Start implementation -----------------------------*/
eMBErrorCode
eMBMasterInit(eMBMode eMode, UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity)
{
    eMBErrorCode eStatus = MB_ENOERR;

    switch (eMode) {
#if MB_MASTER_RTU_ENABLED > 0
    case MB_RTU:
        pvMBMasterFrameStartCur = eMBMasterRTUStart;
        pvMBMasterFrameStopCur = eMBMasterRTUStop;
        peMBMasterFrameSendCur = eMBMasterRTUSend;
        peMBMasterFrameReceiveCur = eMBMasterRTUReceive;
        pvMBMasterFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBMasterPortClose : NULL;
        pxMBMasterFrameCBByteReceived = xMBMasterRTUReceiveFSM; // 这里是字节接收函数
        pxMBMasterFrameCBTransmitterEmpty = xMBMasterRTUTransmitFSM;
        pxMBMasterPortCBTimerExpired = xMBMasterRTUTimerExpired;

        eStatus = eMBMasterRTUInit(ucPort, ulBaudRate, eParity);
        break;
#endif
#if MB_MASTER_ASCII_ENABLED > 0
    case MB_ASCII:
        pvMBMasterFrameStartCur = eMBMasterASCIIStart;
        pvMBMasterFrameStopCur = eMBMasterASCIIStop;
        peMBMasterFrameSendCur = eMBMasterASCIISend;
        peMBMasterFrameReceiveCur = eMBMasterASCIIReceive;
        pvMBMasterFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBMasterPortClose : NULL;
        pxMBMasterFrameCBByteReceived = xMBMasterASCIIReceiveFSM;
        pxMBMasterFrameCBTransmitterEmpty = xMBMasterASCIITransmitFSM;
        pxMBMasterPortCBTimerExpired = xMBMasterASCIITimerT1SExpired;

        eStatus = eMBMasterASCIIInit(ucPort, ulBaudRate, eParity);
        break;
#endif
    default:
        eStatus = MB_EINVAL;
        break;
    }

    if (eStatus == MB_ENOERR) { // 表示没有错误
        if (!xMBMasterPortEventInit()) { // if里面返回的是true,故这里不会执行
            /* port dependent event module initalization failed. */
            eStatus = MB_EPORTERR;
        } else { // 但是这里会被执行
            eMBState = STATE_DISABLED;
        }
        /* initialize the OS resource for modbus master. */
        vMBMasterOsResInit(); // 这里就是初始化信号量
    }
    return eStatus;
}

eMBErrorCode
eMBMasterClose(void)
{
    eMBErrorCode eStatus = MB_ENOERR;

    if (eMBState == STATE_DISABLED) {
        if (pvMBMasterFrameCloseCur != NULL) {
            pvMBMasterFrameCloseCur();
        }
    } else {
        eStatus = MB_EILLSTATE;
    }
    return eStatus;
}

eMBErrorCode
eMBMasterEnable(void)
{
    eMBErrorCode eStatus = MB_ENOERR;

    if (eMBState == STATE_DISABLED) // 一般是先调用 eMBMasterInit()然后调用 eMBMasterEnable函数
    {
        /* Activate the protocol stack. */
        pvMBMasterFrameStartCur(); // 实际调用的是 eMBMasterRTUStart
        eMBState = STATE_ENABLED;
    } else {
        eStatus = MB_EILLSTATE;
    }
    return eStatus;
}

eMBErrorCode
eMBMasterDisable(void)
{
    eMBErrorCode eStatus;

    if ((eMBState == STATE_ENABLED) || (eMBState == STATE_ESTABLISHED)) {
        pvMBMasterFrameStopCur();
        eMBState = STATE_DISABLED;
        eStatus = MB_ENOERR;
    } else if (eMBState == STATE_DISABLED) {
        eStatus = MB_ENOERR;
    } else {
        eStatus = MB_EILLSTATE;
    }
    return eStatus;
}

BOOL eMBMasterIsEstablished(void)
{
    if (eMBState == STATE_ESTABLISHED) {
        return TRUE;
    } else {
        return FALSE;
    }
}

eMBErrorCode
eMBMasterPoll(void)
{
    static UCHAR* ucMBFrame; // 注意这里的static关键字
    static UCHAR ucRcvAddress;
    static UCHAR ucFunctionCode;
    static USHORT usLength; // 里面存储的是PDU的长度(除去从站地址和CRC校验)
    static eMBException eException;

    int i, j;
    eMBErrorCode eStatus = MB_ENOERR;
    eMBMasterEventType eEvent;
    eMBMasterErrorEventType errorType;

    /* Check if the protocol stack is ready. */
    if ((eMBState != STATE_ENABLED) && (eMBState != STATE_ESTABLISHED)) { // 在函数eMBMasterEnable()中  eMBState变为STATE_ENABLED
        return MB_EILLSTATE; // 也就是说  eMBState只能是两种状态 STATE_ENABLED 或 STATE_ESTABLISHED
    }

    /* Check if there is a event available. If not return control to caller.
     * Otherwise we will handle the event. */
    if (xMBMasterPortEventGet(&eEvent) == TRUE) // 这个函数在portevent_m.c中定义
    { // 主站一直等着
        switch (eEvent) {
        case EV_MASTER_READY: // EV_MASTER_READY     这个事件是串口初始化完成后再过3.5T然后发送的  表示主机已经准备好了
            eMBState = STATE_ESTABLISHED; // 开启串口之后过了3.5t个时间    eRcvState状态会由STATE_M_RX_INIT变为STATE_M_RX_IDLE   并且会发送事件 EV_MASTER_READY
            break;

        case EV_MASTER_FRAME_RECEIVED: // 下面这个函数是朝外读取数据  EV_MASTER_FRAME_RECEIVED  到这里来表示 已经接收到了一个完整的帧
            // xMBMasterRTUReceiveFSM  函数中不断接受,不接受时(发生了超时)就会发送这个事件
            eStatus = peMBMasterFrameReceiveCur(&ucRcvAddress, &ucMBFrame, &usLength); // 这里调用的是eMBMasterRTUReceive()函数
            /* Check if the frame is for us. If not ,send an error process event. usLength指除去从设备地址  和 CRC校验之后的长度
              ucMBFrame指向了功能码*/
            if ((eStatus == MB_ENOERR) && (ucRcvAddress == ucMBMasterGetDestAddress())) { // 没有错误,并且从站地址相同(发送的和接受的)
                (void)xMBMasterPortEventPost(EV_MASTER_EXECUTE); // 这里相当于自己发送事件自己接受事件
                // 接收数据完成之后我就得执行了
            } else {
                // 超时函数中也会发送这个事件
                vMBMasterSetErrorType(EV_ERROR_RECEIVE_DATA); // 设置错误类型
                (void)xMBMasterPortEventPost(EV_MASTER_ERROR_PROCESS); // 注意这里是在函数调用时加了(void),而不是在定义时加
                // 上面加(void)就是告诉编译器不用管函数的返回值  //这里也是相当于自己发送事件自己接受事件
            }
            break;

        case EV_MASTER_EXECUTE: // 通过事件这个IPC来实现系统状态的切换
            ucFunctionCode = ucMBFrame[MB_PDU_FUNC_OFF]; //(指向)modbus数据帧的第二个字节(到底是发送数据帧的还是接收数据帧的  得具体讨论)
            eException = MB_EX_ILLEGAL_FUNCTION;
            /* If receive frame has exception .The receive function code highest bit is 1.*/
            if (ucFunctionCode >> 7) {
                eException = (eMBException)ucMBFrame[MB_PDU_DATA_OFF]; // 错误码是数据里面包含的
            } else { // 这里表示没有错误
                for (i = 0; i < MB_FUNC_HANDLERS_MAX; i++) {
                    /* No more function handlers registered. Abort. */
                    if (xMasterFuncHandlers[i].ucFunctionCode == 0) {
                        break; // MB_FUNC_NONE=0   MB_ADDRESS_BROADCAST=0
                    } else if (xMasterFuncHandlers[i].ucFunctionCode == ucFunctionCode) { // 功能码,对应有功能码handler
                        vMBMasterSetCBRunInMasterMode(TRUE); // 标记为运行在主站模式
                        /* If master request is broadcast,
                         * the master need execute function for all slave.
                         */
                        if (xMBMasterRequestIsBroadcast()) {
                            // 广播   就是批量改 并且还没有回复
                            usLength = usMBMasterGetPDUSndLength(); // 除去 从站地址和CRC校验  这里是发送的
                            for (j = 1; j <= MB_MASTER_TOTAL_SLAVE_NUM; j++) { // 看来对于广播这种模式还得好好研究研究
                                vMBMasterSetDestAddress(j); // 注意上面是j,下面是i    懂了这里相当于回写(自己写自己,因为广播是不会有回复的)
                                eException = xMasterFuncHandlers[i].pxHandler(ucMBFrame, &usLength); // 也就是说同一个功能码会连续处理16个地址不同的从站
                                // 广播时ucMBFrame是指向发送modbus数据帧的
                            }
                        } else {
                            // 这里是单播  那么 ucMBFrame指向的是接收modbus数据帧    usLength指向接收到的数据
                            eException = xMasterFuncHandlers[i].pxHandler(ucMBFrame, &usLength);
                        }
                        vMBMasterSetCBRunInMasterMode(FALSE);
                        break;
                    }
                }
            }
            /* If master has exception ,Master will send error process.Otherwise the Master is idle.*/
            if (eException != MB_EX_NONE) { // 有异常
                vMBMasterSetErrorType(EV_ERROR_EXECUTE_FUNCTION);
                (void)xMBMasterPortEventPost(EV_MASTER_ERROR_PROCESS); // 自己发送自己接收
            } else { // 没有异常
                vMBMasterCBRequestScuuess(); //
                vMBMasterRunResRelease(); // 从机回复后就有了结果(不论是成功还是失败) 那么就可以发送下一帧了
            }
            break;

        case EV_MASTER_FRAME_SENT: ////EV_MASTER_FRAME_SENT   具体的功能码函数最后会发送事件,modbus主站轮询线程会接受这些事件,并执行相应的指令
            /* Master is busy now.   开始发送了*/
            vMBMasterGetPDUSndBuf(&ucMBFrame); // ucMBFrame现在指向了功能码(发送数据帧的)
            eStatus = peMBMasterFrameSendCur(ucMBMasterGetDestAddress(), ucMBFrame, usMBMasterGetPDUSndLength()); // 实际调用eMBMasterRTUSend()函数  xMBMasterRTUTransmitFSM
            break; //

        case EV_MASTER_ERROR_PROCESS: // 这里只是一个笼统的错误   下面三个才是具体的错误
            /* Execute specified error process callback function. */
            errorType = eMBMasterGetErrorType();
            vMBMasterGetPDUSndBuf(&ucMBFrame); // 指向了功能码
            switch (errorType) {
            case EV_ERROR_RESPOND_TIMEOUT: // 响应超时处理函数   这里的函数又会向具体的功能码函数发送事件
                vMBMasterErrorCBRespondTimeout(ucMBMasterGetDestAddress(), ucMBFrame, usMBMasterGetPDUSndLength());
                break;
            case EV_ERROR_RECEIVE_DATA: // 接受数据错误处理函数
                vMBMasterErrorCBReceiveData(ucMBMasterGetDestAddress(), ucMBFrame, usMBMasterGetPDUSndLength());
                break;
            case EV_ERROR_EXECUTE_FUNCTION: // 表明从站返回了错误码
                vMBMasterErrorCBExecuteFunction(ucMBMasterGetDestAddress(), ucMBFrame, usMBMasterGetPDUSndLength());
                break;
            }
            vMBMasterRunResRelease(); // 从机回复后就有了结果(不论是成功还是失败) 那么就可以发送下一帧了
            break;

        default:
            break;
        }
    }
    return MB_ENOERR;
}

/* Get whether the Modbus Master is run in master mode.*/
BOOL xMBMasterGetCBRunInMasterMode(void)
{
    return xMBRunInMasterMode;
}
/* Set whether the Modbus Master is run in master mode.*/
void vMBMasterSetCBRunInMasterMode(BOOL IsMasterMode)
{
    xMBRunInMasterMode = IsMasterMode;
}
/* Get Modbus Master send destination address. */
UCHAR ucMBMasterGetDestAddress(void)
{
    return ucMBMasterDestAddress; // 这里是从机的地址
}
/* Set Modbus Master send destination address. */
void vMBMasterSetDestAddress(UCHAR Address)
{
    ucMBMasterDestAddress = Address;
}
/* Get Modbus Master current error event type. */
eMBMasterErrorEventType eMBMasterGetErrorType(void)
{
    return eMBMasterCurErrorType;
}
/* Set Modbus Master current error event type. */
void vMBMasterSetErrorType(eMBMasterErrorEventType errorType)
{
    eMBMasterCurErrorType = errorType;
}

#endif
